//
// Created by tiany on 2025/2/12.
//

#include <string.h>

#include "bootloader_cfg.h"
#include "bootloader_app.h"
#include "fal.h"

#include "stm32f4xx_hal.h"

const struct fal_partition *User_OTA_Info = NULL;

OTA_InfoCB OTA_Info;
UpDate_CB UpDate;

void OTA_Update_Complete(void)
{
  if ( User_OTA_Info != NULL)
  {
	fal_partition_erase(User_OTA_Info, OTA_USER_INFO_MEMORY_ADDRESS, OTA_INFO_SIZE);
	OTA_Info.OTA_Flag = 0xAABBCCDD;
	fal_partition_write(User_OTA_Info, OTA_USER_INFO_MEMORY_ADDRESS, (uint8_t *)&OTA_Info, OTA_INFO_SIZE);
  }
}

void OTA_Read_Info(void) {
  User_OTA_Info = fal_partition_find("user");
  memset(&OTA_Info, 0, OTA_INFO_SIZE);
  fal_partition_read(User_OTA_Info, OTA_USER_INFO_MEMORY_ADDRESS, (uint8_t *)&OTA_Info, OTA_INFO_SIZE);
}

void Bootloader_Beacon(void) {
  printf("[D/OTA] Info:%lx FLAG:%x\n", OTA_Info.OTA_Flag, OTA_SET_FLAG);
  if (OTA_Info.OTA_Flag == OTA_SET_FLAG) {
	printf("OTA update......\n");
	OTA_Update_Complete();
  } else {
	printf("app run......\n");
	boot_load_app();
  }
}

void Bootloader_loop(void) {
  // Test_OTA_Wrtie_Info();
  OTA_Read_Info();
  Bootloader_Beacon();
}

/**
 * 【boot_load_app】
 * 说明:
 * 该函数用于加载并跳转到应用程序（APP）。通过检查应用程序地址的栈顶地址是否合法，初始化堆栈指针并跳转到应用程序入口。
 *
 * 过程:
 * 1. 检查应用程序地址的栈顶地址是否合法，如果合法则进行后续的初始化与跳转。
 * 2. 关闭全局中断，防止在初始化过程中出现中断干扰。
 * 3. 关闭系统滴答定时器并重置其值。
 * 4. 关闭时钟，恢复到默认状态。
 * 5. 关闭所有中断并清除所有中断挂起标志。
 * 6. 恢复全局中断。
 * 7. 设置处理器为特权模式，并使用主堆栈指针（MSP）初始化堆栈指针。
 * 8. 调用应用程序的入口点（APP入口函数），开始执行应用程序。
 * 9. 如果跳转失败，进入死循环，防止继续执行不合法的代码。
 */
void boot_load_app(void)
{
  int i = 0;
  void (*SysMemAPPJump)(void);

  __IO uint32_t APP_Addr = USER_APP_RUN_ADDRESS;

  if(((*(volatile uint32_t*)APP_Addr)&0xFF000000) == 0x20000000)  // 检查栈顶地址是否合法
  {
	SysMemAPPJump = (void (*)(void)) (*(volatile uint32_t*) (APP_Addr + 4));
	/* 关闭全局中断 */
	__set_PRIMASK(1);

	/* 关闭滴答定时器，复位到默认值 */
	SysTick->CTRL = 0;
	SysTick->LOAD = 0;
	SysTick->VAL = 0;

	/* 设置所有时钟到默认状态 */
	HAL_RCC_DeInit();

	/* 关闭所有中断，清除所有中断挂起标志 */
	for (i = 0; i < 8; i++)
	{
	  NVIC->ICER[i] = 0xFFFFFFFF;
	  NVIC->ICPR[i] = 0xFFFFFFFF;
	}

	/* 使能全局中断 */
	__set_PRIMASK(0);

	/* 设置为特权级模式，使用 MSP 指针 */
	__set_CONTROL(0);

	__set_MSP(*(volatile uint32_t*)APP_Addr);  // 初始化 APP 堆栈指针

	SysMemAPPJump();

	while (1)
	{
	  printf("[B/D] App Run Fail\n");
//	  HAL_Delay(1000);
	  // 如果跳转失败，处理死循环
	}
  }
}